package com.gitee.huanminabc.jcommon.multithreading;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 基于 key 的互斥锁实现，同一个 key 只能一个线程执行。
 * <p>
 * <b>功能说明：</b><br>
 * - 支持对任意字符串 key 进行锁隔离，适合分组并发场景。<br>
 * - 支持同一线程的可重入锁（同一线程可以多次获取同一 key 的锁）。<br>
 * - 自动管理锁对象的生命周期，当锁完全释放且无等待线程时自动清理，避免内存泄漏。<br>
 * - 线程安全，适合高并发环境。
 * <br>
 * <b>典型用法：</b>
 * <pre>
 *   SimpleLockByKey lock = new SimpleLockByKey();
 *   // 方式1：手动加锁解锁
 *   lock.lock("user:123");
 *   try {
 *       // 执行需要互斥的操作
 *   } finally {
 *       lock.unlock("user:123");
 *   }
 *   // 方式2：使用函数式接口（推荐）
 *   lock.lock("user:123", () -> {
 *       // 执行需要互斥的操作
 *   });
 * </pre>
 * <b>注意事项：</b><br>
 * - 每次加锁后必须配对调用 unlock，否则会导致死锁。<br>
 * - 支持同一线程重入，但必须配对解锁。<br>
 * - 不支持跨线程解锁，只能由持有锁的线程释放。<br>
 * - 锁对象会在完全释放后自动清理，无需手动管理。
 *
 * @author huanmin
 * @date 2024/6/5 下午1:01
 */

public class SimpleLockByKey implements LockKey<String> {
    private final Map<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();


    /**
     * 加锁
     */
    @Override
    public void lock(String key) {
        // 如果key为空，直接返回
        if (key == null) {
            throw new IllegalArgumentException("key 不能为空");
        }
        // 获取或创建一个ReentrantLock对象
        ReentrantLock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock());
        // 获取锁
        lock.lock();
    }

    @Override
    public boolean tryLock(String key) {
        ReentrantLock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock());
        return  lock.tryLock();
    }


    @Override
    public boolean tryLock(String key, long timeout, TimeUnit unit) {
        ReentrantLock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock());
        try {
           return lock.tryLock(timeout, unit);
        } catch (InterruptedException e) {
            // 恢复中断状态
            Thread.currentThread().interrupt();
            return false;
        }
    }


    /**
     * 解锁
     */
    @Override
    public void unlock(String key) {
        // 如果key为空，直接返回
        if (key == null) {
            throw new IllegalArgumentException("key 不能为空");
        }
        // 从Map中获取锁对象
        ReentrantLock lock = lockMap.get(key);
        // 获取不到锁就不解锁了,表示要么没加锁成功导致其他线程已经把锁释放了, 要不就是key不存在
        if (lock == null) {
           return;
        }
        // 其他线程非法持有不允许释放
        if (!lock.isHeldByCurrentThread()) {
            throw new IllegalStateException("当前线程尚未持有，key:" + key + "的锁，不允许释放");
        }
        lock.unlock();
        
        // 锁清理：当锁完全释放（重入次数为0）且没有等待线程时，从map中移除锁对象，避免内存泄漏
        if (lock.getHoldCount() == 0 && !lock.hasQueuedThreads()) {
            lockMap.remove(key, lock);
        }
    }

}